package com.jingfu.Service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.jingfu.Dao.UserDao;
import com.jingfu.Service.UserService;
import com.jingfu.common.BadRequestException;
import com.jingfu.common.ErrorCode;
import com.jingfu.dto.*;
import com.jingfu.mapper.*;
import com.jingfu.pojo.SysRole;
import com.jingfu.pojo.SysUser;
import com.jingfu.pojo.SysUserRole;
import com.jingfu.util.SpringUtils;
import com.jingfu.util.UserUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.credential.PasswordService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.sql.Struct;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author jingfuu@163.com
 * @version v1.0
 * @date 2021/5/14 2:40 下午
 * @description 用户service
 **/
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {
    @Autowired
    SysUserMapper userMapper;

    @Autowired
    SysDeptMapper sysDeptMapper;

    @Autowired
    SysPowerMapper sysPowerMapper;

    @Autowired
    SysRoleMapper sysRoleMapper;

    @Autowired
    SysUserMapper sysUserMapper;

    @Autowired
    SysUserRoleMapper sysUserRoleMapper;

    @Autowired
    UserDao userDao;

    private final String IMG_UPLOAD_PATH = "/upload";

    @Override
    public void login(SysUser sysUser) {
        SysUser realUser = userMapper.selectByUserName(sysUser.getUsername());
        if (Objects.isNull(realUser)) {
            throw new RuntimeException("用户不存在！");
        }
        if (!sysUser.getPassword().equals(realUser.getPassword())) {
            throw new RuntimeException("密码错误！");
        }
    }

    /**
     *  获取用户详情，菜单，权限，部门信息，并放入到缓存中，登录后，每次访问后台接口，getUserDetail((String) subject.getPrincipal())放入到ThreadLocal中
     * @param username
     * @return
     */
    @Override
    public UserDetail getUserDetail(String username) {
        //获取基于内存的缓存对象
        Optional<UserDetail> userDetailOptional = UserUtil.getCacheUserByName(username);
        if (userDetailOptional.isPresent()) {
            return userDetailOptional.get();
        }
        UserDetail userDetail = userMapper.queryUserDetailByUsername(username);
        //查出该用户拥有的所有权限
        List<SysPowerDTO> sysPowerDTOS = sysPowerMapper.findAllByUserId(userDetail.getUserId());
        Set<String> perissions = sysPowerDTOS.stream()
                .filter(item-> StrUtil.isNotBlank(item.getPowerCode()))
                .map(item -> item.getPowerCode())
                .collect(Collectors.toSet());
        //递归整理菜单权限
        userDetail.setMenus(getMenuDTOS(sysPowerDTOS));
        userDetail.setPermissions(perissions);
        UserUtil.cacheUser(username,userDetail);
        return userDetail;
    }

    @Override
    public List<SysPowerDTO.MenuDTO> getMenuListForCurrentUser() {
        Optional<UserDetail> currentUser = UserUtil.getCurrentUser();
        UserDetail userDetail = currentUser.orElseThrow(() -> new BadRequestException(ErrorCode.UNAUTHORIZED));
        return userDetail.getMenus();
    }

    @Override
    public List<UserDTO> getUserList(UserDTO userDTO) {
        List<UserDTO> userDTOS = userMapper.queryUserList(userDTO);
        return userDTOS;
    }

    @Override
    public List<SysRole> queryAllRoles() {
        return  sysRoleMapper.queryAllRles();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createOrUpdateUser(UserAddDTO userAddDTO) {
        List<SysUserRole> sysUserRoleList = Arrays.stream(userAddDTO.getRoleIds().split(",")).map(roleId -> {
            SysUserRole userRole = new SysUserRole();
            userRole.setId(IdUtil.objectId());
            userRole.setRoleId(roleId);
            return userRole;
        }).collect(Collectors.toList());
        if (StrUtil.isNotBlank(userAddDTO.getUserId())) {
            //更新
            //用户名更新，不能在数据库重复
            UserDTO userDTO = userMapper.queryUserDTOById(userAddDTO.getUserId());
            if (!StrUtil.equals(userAddDTO.getUsername(),userDTO.getUsername())){
                checkUsername(userAddDTO.getUsername());
            }
            SysUser sysUser = new SysUser();
            BeanUtils.copyProperties(userAddDTO,sysUser,"password");
            sysUserMapper.updateByPrimaryKeySelective(sysUser);
            //角色关系先删除再插入
            sysUserRoleMapper.deleteByUserId(sysUser.getUserId());
            Arrays.stream(userAddDTO.getRoleIds().split(",")).forEach(roleId->{
                SysUserRole userRole = new SysUserRole();
                userRole.setId(IdUtil.objectId());
                userRole.setUserId(sysUser.getUserId());
                userRole.setRoleId(roleId);
                sysUserRoleMapper.insertSelective(userRole);
            });
            //删除对应用户的登录缓存信息
            UserUtil.clearUserCache(sysUser.getUsername());
            return;
        }
        //新增用户
        //检查登录名必须唯一
        checkUsername(userAddDTO.getUsername());
        SysUser sysUser = new SysUser();
        BeanUtils.copyProperties(userAddDTO,sysUser,"password");
        PasswordService passwordService = SpringUtils.getBean(PasswordService.class);
        String pass = passwordService.encryptPassword(userAddDTO.getPassword());
        sysUser.setPassword(pass);
        sysUser.setEnable("1");
        sysUser.setStatus("1");
        sysUser.setAvatar(IMG_UPLOAD_PATH+"/default-avatar.jpeg");
        String userId = IdUtil.objectId();
        sysUser.setUserId(userId);
        sysUser.setCreateTime(Date.from(Instant.now()));
        sysUser.setUpdateTime(Date.from(Instant.now()));
        sysUserMapper.insertSelective(sysUser);
        sysUserRoleList.forEach(sysUserRole -> {
            sysUserRole.setUserId(userId);
            sysUserRoleMapper.insertSelective(sysUserRole);
        });


    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeUser(String userId) {
        SysUser sysUser = sysUserMapper.selectByPrimaryKey(userId);
        if (Objects.isNull(sysUser)) {
            throw new BadRequestException(ErrorCode.PARAM_IS_ERROR);
        }
        sysUser.setStatus("0");
        sysUserMapper.updateByPrimaryKey(sysUser);
        UserUtil.clearUserCache(sysUser.getUsername());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void userEnable(String userId, String enable) {
        SysUser sysUser = sysUserMapper.selectByPrimaryKey(userId);
        if (Objects.isNull(sysUser)) {
            throw new BadRequestException(ErrorCode.PARAM_IS_ERROR);
        }
        sysUser.setEnable(enable);
        int i = sysUserMapper.updateByPrimaryKey(sysUser);
        if (i == 1) {
            //修改用户相关信息后，更新缓存
            UserUtil.clearUserCache(sysUser.getUsername());
        }
    }

    @Override
    public List<SysPowerDTO.MenuDTO> getPermAll() {
        List<SysPowerDTO> sysPowerDTOS = sysPowerMapper.findAll();
        return getMenuDTOS(sysPowerDTOS);
    }

    @Override
    public void updateUserBaseInfo(UserAddDTO userAddDTO) {
        SysUser sysUser = sysUserMapper.selectByPrimaryKey(userAddDTO.getUserId());
        if (Objects.isNull(sysUser)) {
            throw new BadRequestException(ErrorCode.PARAM_IS_ERROR);
        }
        if (!StrUtil.equals(userAddDTO.getUsername(),userAddDTO.getUsername())){
            checkUsername(userAddDTO.getUsername());
        }
        if (StrUtil.isNotBlank(userAddDTO.getUsername())) {
            sysUser.setUsername(userAddDTO.getUsername());
        }
        if (StrUtil.isNotBlank(userAddDTO.getRealName())) {
            sysUser.setRealName(userAddDTO.getRealName());
        }
        if (StrUtil.isNotBlank(userAddDTO.getEmail())) {
            sysUser.setEmail(userAddDTO.getEmail());
        }
        if (StrUtil.isNotBlank(userAddDTO.getPhone())) {
            sysUser.setPhone(userAddDTO.getPhone());
        }
        if (StrUtil.isNotBlank(userAddDTO.getSex())) {
            sysUser.setSex(userAddDTO.getSex());
        }
        sysUserMapper.updateByPrimaryKeySelective(sysUser);
        UserUtil.clearUserCache(sysUser.getUsername());
    }

    @Override
    public void userPassEdit(PassEditDTO passEditDTO) {
        SysUser sysUser = sysUserMapper.selectByPrimaryKey(passEditDTO.getUserId());
        if (Objects.isNull(sysUser)) {
            throw new BadRequestException(ErrorCode.PARAM_IS_ERROR);
        }
        //比对原始密码是否正确
        PasswordService passwordService = SpringUtils.getBean(PasswordService.class);
        if (!passwordService.passwordsMatch(passEditDTO.getPassword(),sysUser.getPassword())) {
            throw new BadRequestException(ErrorCode.PASSWORD_ERROR);
        }
        sysUser.setPassword(passwordService.encryptPassword(passEditDTO.getNewPassword()));
        sysUserMapper.updateByPrimaryKeySelective(sysUser);
        UserUtil.clearUserCache(sysUser.getUsername());
    }

    /**
     * 菜单&角色整理，处理成前端展示的树形结构并排序
     * @param sysPowerDTOS
     * @return
     */
    private List<SysPowerDTO.MenuDTO> getMenuDTOS(List<SysPowerDTO> sysPowerDTOS) {
        /**
         * 转换成给前端渲染的DTO
         */
        List<SysPowerDTO.MenuDTO> menuDTOList = sysPowerDTOS.stream().map((sysPowerDTO -> new SysPowerDTO.MenuDTO(sysPowerDTO))).collect(Collectors.toList());


        List<SysPowerDTO.MenuDTO> firstLevelMenus = menuDTOList.stream()
                .sorted(Comparator.comparing(SysPowerDTO.MenuDTO::getSort))
                .filter(sysPowerDTO -> "0".equals(sysPowerDTO.getParentId())).collect(Collectors.toList());

        List<SysPowerDTO.MenuDTO> otherLevelMenus = menuDTOList.stream()
                .filter(sysPowerDTO -> !"0".equals(sysPowerDTO.getParentId())).collect(Collectors.toList());
        //递归整理菜单权限
        doHandlerMenus(firstLevelMenus,otherLevelMenus);
        return firstLevelMenus;
    }

    /**
     * 递归处理菜单权限关系
     * @param firstLevelMenus 父级菜单
     * @param otherLevelMenus 其他菜单
     */
    private void doHandlerMenus(List<SysPowerDTO.MenuDTO> firstLevelMenus, List<SysPowerDTO.MenuDTO> otherLevelMenus) {
        firstLevelMenus.forEach(sysPowerDTO -> {
                    Iterator<SysPowerDTO.MenuDTO> iterator = otherLevelMenus.iterator();
                    while (iterator.hasNext()) {
                    SysPowerDTO.MenuDTO next = iterator.next();
                    if (next.getParentId().equals(sysPowerDTO.getId())) {
                    sysPowerDTO.getChildren().add(next);
                    iterator.remove();
                    }
                }
        });
        if (otherLevelMenus.isEmpty()){
            return;
        }
        firstLevelMenus.stream().filter(sysPowerDTO -> !sysPowerDTO.getChildren().isEmpty()).forEach(sysPowerDTO -> {
            List<SysPowerDTO.MenuDTO> collect = sysPowerDTO.getChildren().stream()
                    .sorted(Comparator.comparing(SysPowerDTO.MenuDTO::getSort))
                    .collect(Collectors.toList());
            sysPowerDTO.setChildren(collect);
            doHandlerMenus(sysPowerDTO.getChildren(), otherLevelMenus);
        });
    }

    private void checkUsername(String username) {
        if (Objects.nonNull(sysUserMapper.selectByUserName(username))) {
            throw new BadRequestException("ACCOUNT_REPEATED","账号重复");
        }
    }

    @Override
    public void userAvatarUpload(MultipartFile file, String userId, String fileName, HttpServletRequest request) {
        String uploadPath = request.getServletContext().getRealPath(IMG_UPLOAD_PATH)+ File.separator + fileName;
        String imgUrl = IMG_UPLOAD_PATH+"/"+fileName;
        if (Objects.isNull(file)) {
            throw new BadRequestException(ErrorCode.USER_AVATAR_UPLOAD_FAIL);
        }
        //将文件存储到指定路径
        try {
            file.transferTo(new File(uploadPath));
        } catch (IOException e) {
            log.error("用户头像上传异常",e);
            throw new BadRequestException(ErrorCode.USER_AVATAR_UPLOAD_FAIL);
        }
        //上传成功后，设置图片路径
        SysUser sysUser = new SysUser();
        sysUser.setUserId(userId);
        sysUser.setAvatar(imgUrl);
        sysUserMapper.updateByPrimaryKeySelective(sysUser);
        //更新用户头像后更新用户缓存里的头像url
        UserUtil.getCurrentUser().get().setAvatar(imgUrl);
    }
}
